/* NOTICE
    OpenJaWS - Open Java Weather Station
    
    Copyright (C) 2008 Grant Gardner <grant@lastweekend.com.au>
    
    OpenJaWS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
NOTICE */

package au.com.lastweekend.openjaws;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.lastweekend.openjaws.api.WeatherStationConfigurer;
import au.com.lastweekend.openjaws.api.WeatherStationException;
import au.com.lastweekend.openjaws.units.PressureUnit;
import au.com.lastweekend.ws2300.WS2300;
import au.com.lastweekend.ws2300.WS2300.HistoryInfo;

public class WS2300Configurer implements WeatherStationConfigurer {

    private static final Logger LOG = LoggerFactory.getLogger(WS2300Configurer.class);

    private Integer historyInterval = null;
    private Double pressureOffset = null;
    private WS2300 ws2300;
    private boolean pressureOffsetDirty = false;
    private boolean historyIntervalDirty;

    public WS2300Configurer(WS2300 ws2300) {

        this.ws2300 = ws2300;
    }

    public String getFormName() {

        return "ws2300";
    }

    public void save() throws WeatherStationException {

        try {
            saveHistoryInterval();
            savePressureCorrection();
        } catch (IOException e) {
            throw new WeatherStationException("Error writing configuration to station", e);
        }

    }

    private void savePressureCorrection() throws IOException {

        if (pressureOffsetDirty) {
            ws2300.setAirPressureCorrection(pressureOffset, PressureUnit.HECTOPASCAL);
            pressureOffsetDirty = false;
        }
    }

    public Date getCurrentTime() throws IOException {

        return ws2300.getCurrentTime();
    }

    public HistoryInfo getHistoryInfo() throws IOException {

        return ws2300.getHistoryInfo();
    }

    public Integer getHistoryInterval() {

        if (historyInterval == null) {
            try {
                historyInterval = ws2300.getHistoryInfo().getInterval();
            } catch (IOException e) {
                LOG.warn("Unable to retrieve current history interval", e);
            }

        }
        return historyInterval;
    }

    public void setHistoryInterval(Integer historyInterval) {

        if (historyInterval <= 0) {
            throw new IllegalArgumentException("Out of range");
        }

        if (this.historyInterval == null || !this.historyInterval.equals(historyInterval)) {
            LOG.info("Setting history interval " + historyInterval + " " + this.historyInterval);

            this.historyInterval = historyInterval;
            historyIntervalDirty = true;

        }
    }

    private void saveHistoryInterval() throws IOException {

        // Align to "on the hour" unless the new interval
        // is less than an hour.
        if (historyIntervalDirty) {
            int minutesToNextReading;
            Calendar now = ws2300.getCalendarTime();
            int minute = now.get(Calendar.MINUTE);

            if (historyInterval >= 60) {
                minutesToNextReading = 60 - minute;
            } else if (60 % historyInterval == 0) {
                minutesToNextReading = historyInterval - (minute % historyInterval);
            } else {
                minutesToNextReading = 1;
            }
            LOG.info("resetHistory(" + historyInterval + "," + minutesToNextReading + ") currentMinute=" + minute);
            ws2300.resetHistoryInfo(historyInterval, minutesToNextReading);
        }
    }

    /**
     * TODO: Units!! (this assumes hPa)
     * 
     * @return the pressure offset in hPa
     */
    public Double getPressureOffset() {

        if (pressureOffset == null) {
            try {
                pressureOffset = ws2300.getAirPressureCorrection().getValue();
            } catch (IOException e) {
                LOG.warn("Unable to retrieve air pressure offset", e);
            }
        }
        return pressureOffset;
    }

    public void setPressureOffset(Double pressureOffset) {

        // TODO Validate offset range.
        if (this.pressureOffset == null || !this.pressureOffset.equals(pressureOffset)) {
            this.pressureOffset = pressureOffset;
            pressureOffsetDirty = true;
        }

    }

}
